#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <pthread.h>
#define SIZ 128
#define THREAD_NUM 4
struct thread_info {
    pthread_t tid[THREAD_NUM];
} threads;

int find(struct thread_info threads, pthread_t tid)
{
    int i;
    for (i = 0; i < THREAD_NUM; i++) {
        if (pthread_equal(threads.tid[i],tid)) {
            return i;
        }
    }
}

int server_process(char* sock_path, int* serv)
{
    int server, client, t, len;
    struct sockaddr_un local, remote;
    char *str = malloc(SIZ*sizeof(char));
    if ((server = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("Socket");
        exit(1);
    }
    *serv = server;
    local.sun_family = AF_UNIX;
    strcpy(local.sun_path, sock_path);
    unlink(local.sun_path);
    len = strlen(local.sun_path) + sizeof(local.sun_family);
    if (bind(server, (struct sockaddr *)&local, len) == -1) {
        perror("Bind");
        exit(1);
    }
    if (listen(server, 5) == -1) {
        perror("Llisten");
        exit(1);
    }
    printf("Waiting for a connection...\n");
    t = sizeof(remote);
    if ((client = accept(server, (struct sockaddr *)&remote, &t)) == -1) {
        perror("Accept");
        exit(1);
    }
    printf("Connected.\n");

    return client;
}

int client_process(char* sock_path)
{
    int sock, t, len;
    struct sockaddr_un remote;
    if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }
    printf("Trying to connect thread %s...\n", sock_path+2);
    remote.sun_family = AF_UNIX;
    strcpy(remote.sun_path, sock_path);
    len = strlen(remote.sun_path) + sizeof(remote.sun_family);
    while(connect(sock, (struct sockaddr *)&remote, len) == -1) {
        perror("Connect");
        sleep(1);
    }
    printf("Connected to thread %s.\n", sock_path+2);
    return sock;
}

void* master(void* args)
{
    unsigned long pid = (unsigned long) pthread_self();
    char sock_path[SIZ];
    sprintf(sock_path, "./%lu", pid);

    struct thread_info* thinfo = (struct thread_info*)args; 

    int pos = find(*thinfo,pthread_self());
    printf("Master Thread id: %lu, position is %d\n",pid,pos);

    char* str = malloc(SIZ*sizeof(char));
    int server, client;

    char* msg = "hello";

    client = server_process(sock_path, &server);

    int n = read(client, str, SIZ);
    str[n] = '\0';
    printf("Master Thread %lu receives a message: %s\n", pid, str);
    if (write(client, msg,strlen(msg)) == -1) {
        perror("send");
        exit(1);
    }
    printf("Master Thread %lu sends a message: %s\n",pid,msg);

    close(server);
    remove(sock_path);

    pthread_exit(NULL);
}

void* slave(void* args){
    
    unsigned long pid = (unsigned long) pthread_self();
    struct thread_info* thinfo = (struct thread_info*)args; 
    int pos = find(*thinfo,pthread_self());
 
    printf("Thread slave id: %lu, position is %d\n",pid,pos); /**/ 

    int i;
    for (i = 0; i < THREAD_NUM; i++) {
        if (i != pos) {
            int sock;
            char* str = malloc(SIZ*sizeof(char));
            char* msg = "hello";
            char sock_path[SIZ];
            sprintf(sock_path, "./%lu", (unsigned long)(thinfo->tid[i]));

            printf("In slave, sock-path : %s\n",sock_path);

            sock = client_process(sock_path);
            if (write(sock, msg,strlen(msg)) == -1) {
                perror("send");
                exit(1);
            }
            printf("Slave Thread %lu sends a message: %s\n",pid,msg);

            int n = read(sock, str, SIZ);
            str[n] = '\0';
            printf("Slave Thread %lu receives a message: %s\n", pid, str);
            close(sock);
        }
    }
    pthread_exit(NULL);
}


int main(int argc, const char *argv[])
{

    if(pthread_create(&(threads.tid[0]),NULL,master,(void*)(&threads)) != 0) {
        printf("Thread1 creation error!\n");
        exit(1);
    }
    if(pthread_create(&(threads.tid[1]),NULL,slave,(void*)(&threads)) != 0) {
        printf("Thread2 creation error!\n");
        exit(1);
    }
    if(pthread_create(&(threads.tid[2]),NULL,slave,(void*)(&threads)) != 0) {
        printf("Thread2 creation error!\n");
        exit(1);
    }
    if(pthread_create(&(threads.tid[3]),NULL,slave,(void*)(&threads)) != 0) {
        printf("Thread2 creation error!\n");
        exit(1);
    }

    int i = 0;
    for (i = 0; i < THREAD_NUM; i++) {
        printf("Thread %d id: %lu\n",i,threads.tid[i]);
    }

    while(1){
        sleep(1);
    }
    
    return 0;
}
